<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue实现audio音频组件</title>
    <style>

    .audioContent{
        position: relative;
        width:100%;
        overflow: hidden;
        box-shadow:2px 2px 5px #c9c9c9;
        border-bottom-left-radius: 6px;
        border-bottom-right-radius: 6px;
        padding-bottom:10px;
    }
    .btnContent{
        float:left;
        position: relative;
        overflow: hidden;
        width:100%;
        height:48px;
    }
    .btnContentLeft,.btnContentRight{
        margin-top:10px;
    }
    .btnContentLeft{
        float:left;
        width:62%;
        overflow: hidden;
    }
    .btnContentRight{
        float:right;
        width:30%;
        overflow: hidden;
        min-width: 150px;
    }
    .audio,.progress,.audioTimer{
        float:left;
        width:100%;
    }

        /*
    .volumeController {
        width: 500px;
        height: 86px;
        margin: 100px auto;
        position: relative;
    }
    .volumeBar {
        width: 500px;
        height: 20px;
        border-radius: 10px;
        background: #9acfea;
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        margin: auto;
        cursor: pointer;
    }
    .volumeBox {
        width: 30px;
        height: 30px;
        background: #ec971f;
        position: absolute;
        bottom: 0;
        top: 0;
        margin: auto 0;
        border-radius: 50%;
        cursor: pointer;
        transition: left 0.1s linear 0s;
    }*/

        /* 音量调节滑块 */
        .volumeController {
            width: 100px;
            height: 40px;
            position: relative;
            float:left;
        }

        .volumeBar {
            width: 100px;
            height: 6px;
            border-radius: 3px;
            background: #9acfea;
            position: absolute;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
            margin: auto;
            cursor: pointer;
        }

        .volumeBox {
            width: 12px;
            height: 12px;
            background: #ec971f;
            position: absolute;
            bottom: 0;
            top: 0;
            margin: auto 0;
            border-radius: 50%;
            cursor: pointer;
            transition: left 0.1s linear 0s;
        }

        /* chrome下 视频进度条样式*/
        progress {
            width: 100%;
            height: 6px;
            color: rgb(255, 51, 0);
            background: #EFEFF4;
            border-radius: 0.1rem;
        }

        /*表示总长度背景色*/
        progress::-webkit-progress-bar {
            background-color: #d4d1d1;
            border-radius: 0.2rem;
        }

        /*表示已完成进度背景色*/
        progress::-webkit-progress-value {
            background: #f13405;
            border-radius: 0.2rem;
        }

        /* css3 border实现播放器按钮 */
        .playContainer {
            float:left;
            overflow: hidden;
        }
        .playContainer li {
            position: relative;
            float: left;
            border: 18px solid #fc4424;
            color: #fc4424;
            height: 0;
            width: 0;
            -webkit-border-radius: 100%;
            -moz-border-radius: 100%;
            -o-border-radius: 100%;
            border-radius: 100%;
            margin: 0 20px;
            cursor: pointer;
        }
        .playContainer button {
            border-style: solid;
            text-indent: -9999px;
            position: absolute;
            top: -8px;
            left: -3px;
            background: none;
            margin: 0;
            padding-right: 1px;
            outline: none; 
        }
        .playBtn button {
            border-color: transparent transparent transparent #fff;
            border-width: 7px 0 7px 11px;
            width: 0;
            height: 0;
        }
        .pauseBtn button {
            border-color: transparent white;
            border-width: 0 6px;
            height: 15px;
            width: 6px;
            left: -9px;
        } 

        /* 按钮组 */
        .muted{
            width:40px;
            height:40px;
            border:none;
            background: none;
            outline: none;
            float:left;
            position: relative;
            margin-right:0.5%;
            cursor: pointer;
        }
        .mutedRight{
            float:right;
        }
        .muted svg{
            width:100%;
            height:100%;
        }
        .mutedMax svg{
            width: 103%;
            height: 103%;
            position: absolute;
            top: -6%;
            left: -5%;
        }
        .mutedRight{
            float:right;
        }

        .noMarginRight{
            margin-right:0;
        }
    </style>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>

    <div id="audioContent" class="audioContent">
        <audio class="audio" ref="audio" src="resourse/1.mp3">您的浏览器不支持audio元素</audio>
        <progress class="progress" id="playPercent" ref="progress" max=100></progress>

        <!-- 播放时间 -->
        <div class="audioTimer" v-show="showTimerFlag">
            {{currentTime}}/{{duration}}
        </div>

        <!-- 按钮组 -->
        <div class="btnContent">

            <div class="btnContentRight">
                <!-- 静音按钮 -->
                <button type="button" @click="setMute" class="muted noMarginRight" :disabled="loadFlag">
                    <svg v-show="noMute" t="1607577104978" class="icon" viewBox="0 0 1024 1024" version="1.1"
                        xmlns="http://www.w3.org/2000/svg" p-id="3125" xmlns:xlink="http://www.w3.org/1999/xlink">
                        <path
                            d="M225.680461 326.598406c-0.419556-0.019443-0.818645-0.019443-1.237177-0.019443L101.812315 326.578963c-22.753213 0-40.876989 18.24248-40.876989 40.777729l0 286.336424c0 22.534226 18.302855 40.777729 40.876989 40.777729l122.629945 0c0.079818 0 0.119727 0 0.198521 0l0 0.157589 300.289204 194.444551c7.125281 6.108115 16.405645 9.781784 26.526143 9.781784 22.573111 0 40.874943-18.301831 40.874943-40.878013 0-1.87572-0.119727-3.711532-0.360204-5.509481L591.970868 168.58151c0.239454-1.795902 0.360204-3.632737 0.360204-5.509481 0-22.574135-18.302855-40.876989-40.874943-40.876989-9.301853 0-17.884322 3.113921-24.750707 8.343015L225.680461 326.598406zM859.567485 510.524392l91.952248-91.951225c11.495822-11.517311 11.576663-30.558993-0.13917-42.274826-11.795651-11.795651-30.636764-11.755742-42.273802-0.140193l-91.953272 91.953272-91.950202-91.953272c-11.639085-11.616572-30.479175-11.655458-42.275849 0.140193-11.715833 11.715833-11.633968 30.757514-0.13917 42.274826l91.952248 91.951225-91.952248 91.953272c-11.494799 11.515265-11.576663 30.556946 0.13917 42.272779 11.796674 11.796674 30.636764 11.756765 42.275849 0.140193l91.950202-91.951225 91.953272 91.951225c11.636015 11.617595 30.477129 11.657504 42.273802-0.140193 11.715833-11.714809 11.634991-30.757514 0.13917-42.272779L859.567485 510.524392z"
                            p-id="3126"></path>
                    </svg>
                    <svg v-show="!noMute" t="1607577833208" class="icon" viewBox="0 0 1024 1024" version="1.1"
                        xmlns="http://www.w3.org/2000/svg" p-id="6589" xmlns:xlink="http://www.w3.org/1999/xlink">
                        <path
                            d="M218.495828 334.609871c-0.390903-0.019443-0.773619-0.019443-1.164522-0.019443L100.022553 334.590428c-21.744233 0-39.087227 17.448394-39.087227 39.001269l0 273.866415c0 21.551852 17.505699 38.999223 39.087227 38.999223l117.308753 0c0.057305 0 0.113587 0 0.171915 0l0 0.153496 287.22056 185.975668c6.824429 5.842055 15.691377 9.354042 25.370831 9.354042 21.590737 0 39.096437-17.505699 39.096437-39.095413 0-1.794879-0.124843-3.551896-0.354064-5.270027L568.836985 183.473685c0.229221-1.718131 0.354064-3.475148 0.354064-5.269004 0-21.590737-17.505699-39.096437-39.096437-39.096437-8.895601 0-17.105586 2.977821-23.682375 7.979742L218.495828 334.609871z"
                            p-id="6590"></path>
                        <path
                            d="M757.858012 953.491133l0.085958 0.075725c123.876332-107.514689 202.211445-266.13329 202.211445-443.041442 0-177.214121-78.603219-336.062965-202.851011-443.61654l-0.11461 0.11461c-4.963035-3.817955-11.17655-6.109138-17.925255-6.109138-16.197914 0-29.322839 13.133112-29.322839 29.321816 0 6.757914 2.28095 12.981662 6.109138 17.926278l-0.333598 0.342808c0.821715 0.706081 1.641383 1.393743 2.462075 2.119267 1.173732 1.202385 2.452865 2.329045 3.817955 3.321652 110.054535 96.710622 179.51349 238.550071 179.51349 396.578224 0 158.02713-69.458955 299.866578-179.51349 396.577201-1.36509 0.99363-2.644223 2.118244-3.817955 3.321652-0.600681 0.533143-1.212618 1.048889-1.822508 1.564635l0.229221 0.230244c-4.152577 5.058203-6.643304 11.530614-6.643304 18.593474 0 16.188704 13.124925 29.321816 29.322839 29.321816C746.317165 960.134437 752.798786 957.651896 757.858012 953.491133z"
                            p-id="6591"></path>
                        <path
                            d="M713.998085 729.35433l0.23843 0.24764c55.380308-56.43022 89.532129-133.76454 89.532129-219.077577 0-85.409229-34.228569-162.800853-89.704045-219.249493l-0.268106 0.267083c-4.886287-3.64604-10.966773-5.821589-17.543561-5.821589-16.197914 0-29.322839 13.133112-29.322839 29.321816 0 6.566556 2.166339 12.657274 5.822612 17.544585l-0.162706 0.170892c0.773619 0.782829 1.547239 1.584078 2.310625 2.38635 0.075725 0.076748 0.152473 0.171915 0.23843 0.248663 43.3626 45.587268 69.983911 107.248629 69.983911 175.132716 0 67.884087-26.621311 129.544425-69.983911 175.131693-0.085958 0.077771-0.162706 0.171915-0.23843 0.24764-0.706081 0.74599-1.422396 1.471514-2.13871 2.214435l0.144286 0.134053c-3.751441 4.926196-5.976108 11.092639-5.976108 17.754363 0 16.188704 13.124925 29.321816 29.322839 29.321816C702.925912 735.328391 709.072913 733.113957 713.998085 729.35433z"
                            p-id="6592"></path>
                    </svg>
                </button>
                <!-- 音量滑块 -->
                <div ref="volumeController" class="volumeController">
                    <div ref="volumeBar" class="volumeBar">
                        <div ref="volumeBox" class="volumeBox"></div>
                    </div>
                </div>
            </div>

            <div class="btnContentLeft">

                <!-- 重新播放按钮 -->
                <button type="button" @click="resetPlay" class="muted mutedMax mutedRight" :disabled="loadFlag">
                    <svg t="1607657622533" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3112"
                        xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
                        <path
                            d="M512 967.111c-250.311 0-455.111-204.8-455.111-455.111S261.689 56.889 512 56.889 967.111 261.689 967.111 512 762.311 967.111 512 967.111z m0-56.889c221.867 0 398.222-176.355 398.222-398.222S733.867 113.778 512 113.778 113.778 290.133 113.778 512 290.133 910.222 512 910.222z"
                            fill="#515151" p-id="3113"></path>
                        <path d="M341.333 341.333h341.334v341.334H341.333z" fill="#515151" p-id="3114"></path>
                    </svg>
                </button>
                <!-- 播放按钮 -->
                <section class="playContainer mutedRight">
                    <li :class="{'defaultBtn':true,'playBtn':!playFlag,'pauseBtn':playFlag}" @click="play">  
                     <button type="button" title="start" :disabled="loadFlag">Start</button>  
                    </li>  
                </section>  
    
            </div>
        </div>
    </div>

    <script>
        var vue_example = new Vue({
            el: "#audioContent",
            data: {
                audio: null, //audio对象
                progress: null,  //播放进度条对象
                playFlag: false, //是否播放
                currentTime: 0,  //当前播放时间
                duration: 0, //音频总时长
                showTimerFlag: false,    //是否显示播放时间
                loadFlag: false,   //是否预加载成功
                noMute: false,   //是否静音
                /* 拖动音频大小 */
                boxL: null,
                newL: null,
                moveL: null,
                mouseX: null,
                left: null,
                cha: 0,
                index: 0,    //标记状态
                volumeBar: null, //拖动条对象
                volumeBox: null, //拖动杆对象
                volumeController: null,  //当前拖动父层
                evt: null,   //本身的事件
                volumeDefault: 100,    //默认声音大小，0~100
            },
            mounted() {
                //音频控件初始化
                this.initAudio();
            },
            methods: {
                //音频控件初始化
                initAudio() {
                    if (window.HTMLAudioElement) {
                        this.audio = this.$refs.audio;
                        this.progress = this.$refs.progress;
                        this.audio.oncanplay = ()=>{
                            this.audio.addEventListener('ended', this.audioEndedCallback, false);
                            this.audio.addEventListener('play', this.audioPlayCallback, false);
                            this.audio.addEventListener('pause', this.audioPauseCallback, false);
                            this.audio.addEventListener('timeupdate', this.updateProgress, false);
                            this.$refs.progress.onclick = this.progress_gotoPosition;
                            this.loadFlag = false;
                            //初始化音量大小拖动
                            this.initVolumeController();
                        }
                    } else {
                        this.showTimerFlag = false;
                        this.loadFlag = true;
                    }
                },
                //播放、暂停
                play() {
                    if (this.audio.paused) {
                        this.audio.play();
                        this.playFlag = true;
                        this.showTimerFlag = true;
                    } else {
                        this.audio.pause();
                        this.playFlag = false;
                    }
                },
                //播放完毕后，用于恢复初始状态的方法
                audioEndedCallback(ev) {
                    this.audio.currentTime = 0;
                    this.audio.pause();
                    this.playFlag = false;
                    //进度条
                    this.updateProgress();
                },
                //播放时的回调函数
                audioPlayCallback() {

                },
                //暂停时的回调函数
                audioPauseCallback() {

                },
                //播放进度的计算方法
                calcTime(time) {
                    var hour;
                    var minute;
                    var second;
                    hour = String(parseInt(time / 3600, 10));
                    if (hour.length == 1) hour = '0' + hour;
                    minute = String(parseInt((time % 3600) / 60, 10));
                    if (minute.length == 1) minute = '0' + minute;
                    second = String(parseInt(time % 60, 10));
                    if (second.length == 1) second = '0' + second;
                    return hour + ":" + minute + ":" + second;
                },
                //更新播放进度
                updateProgress() {
                    if (!isNaN(this.audio.duration)){
                        var value = Math.round((Math.floor(this.audio.currentTime) / Math.floor(this.audio.duration)) * 100, 0);
                        this.progress.value = value;
                        this.currentTime = this.calcTime(Math.floor(this.audio.currentTime));
                        this.duration = this.calcTime(Math.floor(this.audio.duration));
                    }
                },
                //点击进度条跳转播放进度
                progress_gotoPosition(evt) {
                    var o = window.event || evt;  //写事件兼容

                    /*IE和火狐的兼容，获取鼠标相对于目标事件的父元素的内边界在x坐标上的位置*/
                    var coord_X = o.offsetX || o.layerX;

                    this.audio.currentTime = this.audio.duration * (coord_X / this.progress.clientWidth);

                    //跳转后自动播放
                    this.audio.play();
                    this.playFlag = true;
                },
                //停止播放
                resetPlay() {
                    this.audio.currentTime = 0;
                    this.audio.pause();
                    this.playFlag = false;
                },
                //静音方法
                setMute() {
                    if (!this.audio.muted) {
                        this.audio.muted = true;
                        this.noMute = true;
                        //手动改变滑块位置
                        this.setBoxPosition(0);
                        this.volumeSet(0);
                    } else {
                        this.audio.muted = false;
                        this.noMute = false;
                    }
                },
                //音量控制
                volumeSet(v) {
                    //拖动音量滑块判断是否静音
                    this.isVolumeMinMax(v);
                    if (v >= 100) { 
                        this.audio.volume = 1;
                        return false;
                    }
                    if (v <= 0) { 
                        v = 0; 
                    }
                    this.audio.volume = v / 100;
                },
                //拖动音量滑块判断是否静音
                isVolumeMinMax(v){
                    if(v <= 0){
                        this.audio.muted = true;
                        this.noMute = true;
                    }else{
                        this.audio.muted = false;
                        this.noMute = false;
                    }
                },
                //初始化音量大小拖动
                initVolumeController() {
                    //初始化拖动音频大小位置
                    this.volumeController = this.$refs.volumeController;
                    this.volumeBar = this.$refs.volumeBar;
                    this.volumeBox = this.$refs.volumeBox;
                    this.cha = this.volumeBar.offsetWidth - this.volumeBox.offsetWidth;
                    this.evt = new Event("change");
                    this.volumeBox.addEventListener("mousedown", this.mouseDownclickHandler);
                    document.addEventListener("mousemove", this.mouseMoveclickHandler)
                    document.addEventListener("mouseup", this.mouseUpclickHandler);
                    document.addEventListener("change", this.changeHandler);
                    this.volumeBar.addEventListener("click", this.clickHandler);
                    //初始化音量、拖动位置
                    this.initVolumeDefault();
                },
                //初始化音量、拖动位置
                initVolumeDefault() {
                    var left = this.volumeBar.offsetWidth - this.volumeBox.offsetWidth;
                    this.volumeBox.style.left = left + 'px';
                    this.volumeSet(this.volumeDefault);
                },
                //音量大小拖动，回调函数
                mouseDownclickHandler(e) {
                    this.index = 1;
                    this.boxL = this.volumeBox.offsetLeft;
                    this.mouseX = e.clientX;//鼠标按下拖动的位置
                },
                //音量大小拖动，回调函数
                mouseMoveclickHandler(e) {
                    if (this.index === 1) {
                        this.moveL = e.clientX - this.mouseX;//鼠标移动
                        this.newL = this.boxL + this.moveL;//left值

                        //判断最小值与最大值
                        if (this.newL < 0) {
                            this.newL = 0;
                        }
                        if (this.newL >= this.cha) {
                            this.newL = this.cha;
                        }
                        // 改变left值
                        this.volumeBox.style.left = this.newL + 'px';
                        // 计算比例
                        var bili = this.newL / this.cha * 100;
                        //console.log('当前位置' + Math.ceil(bili) + '%');
                        this.volumeSet(bili);
                        this.evt.elem = this;//当前指向 对象
                        //document.dispatchEvent(this.evt);//朝谁发送 抛发
                    }
                },
                //音量大小拖动，回调函数
                mouseUpclickHandler(e) {
                    this.index = 0;
                    this.evt.elem = this;//当前指向对象
                    //document.dispatchEvent(this.evt);//朝谁发送 抛发
                },
                //音量大小拖动，回调函数
                clickHandler(e) {
                    var left = e.clientX - this.volumeController.offsetLeft - this.volumeBox.offsetWidth / 2;
                    if (left < 0) {
                        left = 0;
                    }
                    if (left >= this.cha) {
                        left = this.cha;
                    }
                    this.volumeBox.style.left = left + 'px';
                    var bili = left / this.cha * 100;
                    //console.log('当前位置'+ Math.ceil(bili) + '%');
                    this.volumeSet(bili);
                    this.evt.elem = this;//当前指向 对象
                    //document.dispatchEvent(this.evt);//朝谁发送 抛发
                },
                //音量大小拖动，回调函数
                changeHandler(e) {
                    console.log(e);
                },
                //手动改变滑块位置
                setBoxPosition(v){
                    // 改变left值
                    this.volumeBox.style.left = v + 'px';
                }
            }
        });
    </script>

</body>

</html>